﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SSharp.Core.DataTypes {
	public class Cons {
		public virtual object car { get; set; }
		public virtual object cdr { get; set; }

		public Cons(object car, object cdr) {
			this.car = car;
			this.cdr = cdr;
		}

		public override bool Equals(object obj) {
			if (obj is Cons) {
				Cons cons = (Cons)obj;
				return EqualsCar(cons) && EqualsCdr(cons);
			} else {
				return false;
			}
		}

		private bool EqualsCar(Cons obj) {
			if (ReferenceEquals(car, null))
				return ReferenceEquals(obj.car, null);

			return car.Equals(obj.car);
		}

		private bool EqualsCdr(Cons obj) {
			if (ReferenceEquals(cdr, null))
				return ReferenceEquals(obj.cdr, null);

			return cdr.Equals(obj.cdr);
		}

		public override int GetHashCode() {
			int carHash = car == null ? 0 : car.GetHashCode();
			int cdrHash = cdr == null ? 0 : cdr.GetHashCode();

			return (carHash << 3) ^ cdrHash;
		}

		public override string ToString() {
			return "(" + internalToString() + ")";
		}

		/// <summary>
		/// The ToString without parenthesis.
		/// This is used for lists, for example.
		/// </summary>
		private string internalToString() {
			if (cdr is List.EmptyList) {
				return ToString(car);
			} else if (cdr is Cons) {
				return ToString(car) + " " + ((Cons)cdr).internalToString();
			} else {
				return ToString(car) + " . " + ToString(cdr);
			}
		}

		private static string ToString(object obj) {
			if (obj == null) {
				return "null";
			} else {
				return obj.ToString();
			}
		}
	}
}
